home *** CD-ROM | disk | FTP | other *** search
/ Collection of Tools & Utilities / Collection of Tools and Utilities.iso / sound / sb01.zip / SB.C < prev    next >
C/C++ Source or Header  |  1994-07-11  |  19KB  |  628 lines

  1. /*
  2.  * Play and record digitized sound sample on soundblaster DAC/ADC using DMA.
  3.  * This source code is in the public domain.
  4.  * 
  5.  * Modification History
  6.  *
  7.  *  9-Nov-93    David Baggett       Wrote it based on Sound Blaster
  8.  *              <dmb@ai.mit.edu>    Freedom project and Linux code.
  9.  *
  10.  *  24-Jun-94   Gerhard Kordmann    - modified for recording facilities
  11.  *                                  - added keyboard safety-routines to
  12.  *                                    allow stopping of playing/recording
  13.  *                                  - removed click while buffer switch 
  14.  *                                  - added bugfixes by Grzegorz Jablonski
  15.  *                                    and several safety checks
  16.  *                                  - added free dosmem at end of program
  17.  *    08-Jul-94   Gerhard Kordmann    - click also removed in recording 
  18.  *                                    - changes from dosmem... to memcpy
  19.  *              <kordmann@ldv01.Uni-Trier.de>
  20.  *              <grzegorz@kmm-lx.p.lod.edu.pl>
  21.  */
  22. #include <sys/types.h>
  23. #include <sys/stat.h>
  24. #include <stdio.h>
  25. #include <dos.h>
  26. #include <string.h>
  27. #include <pc.h>
  28. #include "sb.h"
  29.  
  30.  
  31. /*  GO32 DPMI structs for accessing DOS memory. */
  32. static _go32_dpmi_seginfo dosmem;   /* DOS (conventional) memory buffer */
  33.  
  34. static _go32_dpmi_seginfo oldirq_rm;    /* original real mode IRQ */
  35. static _go32_dpmi_registers rm_regs;
  36. static _go32_dpmi_seginfo rm_si;    /* real mode interrupt segment info */
  37.  
  38. static _go32_dpmi_seginfo oldirq_pm;    /* original prot-mode IRQ */
  39. static _go32_dpmi_seginfo pm_si;    /* prot-mode interrupt segment info */
  40.  
  41. /*  Card parameters  */
  42. unsigned int    sb_ioaddr;
  43. unsigned int    sb_irq;
  44. unsigned int    sb_dmachan;
  45.  
  46. /* Is a sound currently playing or recorded ? */
  47. volatile int    sb_dma_active = 0;
  48.  
  49. /* Conventional memory buffers for DMA. */
  50. static volatile int sb_bufnum = 0;
  51. static char     *sb_buf[2];
  52. static unsigned int sb_buflen[2];
  53.  
  54. /* Info about current sample */
  55. static unsigned char    *sb_curdata;    /* pointer to next bit of data */
  56. static unsigned long    sb_curlength;   /* total length length left to play */
  57.  
  58.  
  59. /* DMA chunk size, in bytes.
  60.  * This parameter determines how big our DMA buffers are.  We play
  61.  * the sample by piecing together chunks that are this big.  This
  62.  * means that we don't have to copy the entire sample down into
  63.  * conventional memory before playing it.  (A nice side effect of
  64.  * this is that we can play samples that are longer than 64K.)
  65.  *
  66.  * Setting this is tricky.  If it's too small, we'll get lots
  67.  * of interrupts, and slower machines might not be able to keep
  68.  * up.  Furthermore, the smaller this is, the more grainy the
  69.  * sound will come out.
  70.  *
  71.  * On the other hand, if we make it too big there will be a noticeable
  72.  * delay between a call to sb_play and when the sound actually starts
  73.  * playing, which is unacceptable for things like games where sound
  74.  * effects should be "instantaneous".
  75.  *
  76.  */
  77. #define DMA_CHUNK (16000)
  78.  
  79. /*
  80.  * Define replacements for DOS enable and disable.
  81.  * Be careful about inlining these -- GCC has a tendency to move
  82.  * them around even if you declare them volatile.  (This is definitely
  83.  * true before 2.5.2; may be fixed in 2.5.2.)
  84.  */
  85. void disable()
  86. {
  87.     __asm__ __volatile__ ("cli");
  88. }
  89. void enable()
  90. {
  91.     __asm__ __volatile__ ("sti");
  92. }
  93.  
  94. /* Interrupt handler for recording
  95.  *
  96.  * This is called in both protected mode and in real mode -- this means
  97.  * we don't have to switch modes when we service the interrupt.
  98.  */
  99. void sb_intr_rec(_go32_dpmi_registers *reg)
  100. {
  101.     register unsigned n = sb_bufnum;        /* buffer we just recorded */
  102.  
  103.     inportb(sb_ioaddr + SB_DSP_DATA_AVAIL); /* Acknowledge soundblaster */
  104.  
  105.     sb_rec_buffer(1 - n);                   /* Start next buffer recording */
  106.     
  107.     sb_empty_buffer(n);                     /* Save this buffer */
  108.     
  109.     outportb(0x20, 0x20);                   /* Acknowledge the interrupt */
  110.  
  111.     enable();
  112. }
  113.  
  114. /* Save buffer n in sb_curdata and calculate size for next time */
  115. void sb_empty_buffer(register unsigned n)
  116. {
  117.     if(sb_buflen[n] > 0) {
  118.         memcpy(sb_curdata, (char *)(0xE0000000+(unsigned long) sb_buf[n]),  sb_curlength);
  119. /*
  120.  *        the original version, sometimes spot of misterious crashes 
  121.  *        dosmemget((unsigned long) sb_buf[n], sb_buflen[n], sb_curdata);
  122.  */
  123.         sb_curdata += sb_buflen[n];
  124.         sb_curlength -= sb_buflen[n];
  125.  
  126.         /* for determination of buffersize keep in mind, that right now  */
  127.         /*  the other buffer is recording up to DMA_CHUNK bytes. Only if */
  128.         /*  - after saving this other buffer - there is still work to do,*/
  129.         /*  then this buffer can take the rest in the next round         */
  130.         if (sb_curlength > DMA_CHUNK) {
  131.             if (sb_curlength > 2*DMA_CHUNK)
  132.                 sb_buflen[n] = DMA_CHUNK;
  133.             else
  134.                 sb_buflen[n] = sb_curlength - DMA_CHUNK;
  135.         }
  136.         else 
  137.             sb_buflen[n] = 0;
  138.     }
  139. }
  140.  
  141. void sb_rec_buffer(register unsigned n)
  142. {
  143.     int     t;
  144.     unsigned char   im, tm;
  145.     
  146.     if (sb_buflen[n] == 0) {                /* See if we're already done */
  147.         sb_dma_active = 0;
  148.         return;
  149.     }
  150.     disable();
  151.  
  152.     im = inportb(0x21);                     /* Enable interrupts on PIC */
  153.     tm = ~(1 << sb_irq);
  154.     outportb(0x21,im & tm);
  155.  
  156.     outportb(SB_DMA_MASK, 5);               /* Set DMA mode to 'record' */
  157.     outportb(SB_DMA_FF, 0);
  158.     outportb(SB_DMA_MODE, 0x45);
  159.     
  160.     sb_bufnum = n;                          /* Set transfer address */
  161.     t = (int) ((unsigned long) sb_buf[n] >> 16) ;
  162.     outportb(SB_DMAPAGE + 3, t & 0xFF);
  163.     t = (int) ((unsigned long) sb_buf[n] & 0xFFFF);
  164.     outportb(SB_DMA + 2 * sb_dmachan, t & 0xFF);
  165.     outportb(SB_DMA + 2 * sb_dmachan, t >> 8);
  166.                                             /* Set transfer length byte count */
  167.     outportb(SB_DMA + 2 * sb_dmachan + 1, (sb_buflen[n]-1) & 0xFF);
  168.     outportb(SB_DMA + 2 * sb_dmachan + 1, (sb_buflen[n]-1) >> 8);
  169.  
  170.     outportb(SB_DMA_MASK, sb_dmachan);      /* Unmask DMA channel */
  171.  
  172.     enable();
  173.  
  174.     sb_writedac(SB_DMA_ADC);            /* command byte for DMA ADC transfer */
  175.  
  176.     sb_writedac((sb_buflen[n]-1) & 0xFF);       /* sb_write length */
  177.     sb_writedac((sb_buflen[n]-1) >> 8);
  178.  
  179.     /* A sound is recorded now. */
  180.     sb_dma_active = 1;                  /* A sound is recorded now. */
  181. }
  182.  
  183. /* Record a sample through the ADC using DMA. */
  184. /* return number of bytes actually recorded */
  185. unsigned long sb_rec(unsigned char *data, unsigned long length)
  186. {
  187.     sb_install_interrupts(sb_intr_rec); /* Install our interrupt handlers */
  188.  
  189.     sb_curdata = data;                      /* Prime the buffers */
  190.     sb_curlength = length;
  191.     if(length > DMA_CHUNK)
  192.         sb_buflen[0] = DMA_CHUNK;
  193.     else
  194.         sb_buflen[0] = length;
  195.  
  196.     if(length <= DMA_CHUNK)
  197.         sb_buflen[1] = 0;
  198.     else
  199.         if(length > 2*DMA_CHUNK)
  200.             sb_buflen[1] = DMA_CHUNK;
  201.         else
  202.             sb_buflen[1] = length-DMA_CHUNK;
  203.     
  204.     sb_rec_buffer(0);               /* Start the first buffer recording.    */
  205.     while (sb_dma_active)
  206.         if(mykbhit()) {             /* kbhit crashed sometimes              */
  207.             int rest;
  208.             rest = sb_read_counter();       /* samples still to record      */
  209.             sb_writedac(SB_HALT_DMA);       /* stop playing                 */
  210.             rest = sb_buflen[sb_bufnum] - rest;/* samples already recorded  */
  211.             length -= (sb_curlength - rest);/* total samples recorded       */
  212.             sb_buflen[sb_bufnum] = rest;    /* save those samples           */
  213.             sb_empty_buffer(sb_bufnum);
  214.             sb_dma_active = 0;              /* and exit the loop            */
  215.             sb_buflen[0] = sb_buflen[1] = sb_curlength = 0; /* clean up     */
  216.         }
  217.     sb_cleanup_ints();                   /* remove interrupts */
  218.     return length;
  219. }
  220.  
  221.  
  222. void sb_intr_play(_go32_dpmi_registers *reg)
  223. {
  224.     register unsigned n = sb_bufnum;        /* buffer we just played */
  225.  
  226.     inportb(sb_ioaddr + SB_DSP_DATA_AVAIL); /* Acknowledge soundblaster */
  227.     
  228.     sb_play_buffer(1 - n);                  /* Start next buffer player */
  229.     
  230.     sb_fill_buffer(n);